<template>
  <div>
    <a-card :bodyStyle="{display:'flex',padding:'0px'}" style="margin:5px;" class="">
      <template slot="title" class="title">
        {{modename}}
        <a-divider type="vertical" />
        <a-icon
          v-if="runing"
          @click="stopCode"
          title="停止运行"
          type="pause-circle" class="anticon anticon-ok" />
        <a-icon
          v-else
          @click="runCode"
          title="点击运行"
          type="play-circle" class="anticon anticon-ok" />
        <a-divider type="vertical" />
        <a-icon 
          @click="$refs.save.init()"
          title="保存模型"
          type="save" class="anticon anticon-ok" />
        <a-divider type="vertical" />
        <a-icon
          v-if="!islocal"
          @click="()=>{
            $message.loading('loading...',0.5).then(()=>{
              setStore(modename,{modelInfo:this.modelInfo});islocal=true;
            })
          }"
          title="添加缓存"
          type="cloud-upload" class="anticon anticon-ok" />
        <a-icon
          v-else
          @click="()=>{
            $message.loading('loading...',0.5).then(()=>{
              removeStore(modename);islocal=false;
            })
          }"
          title="清楚缓存"
          type="delete" class="anticon anticon-ok" />
        <a-divider type="vertical" />
        <a-icon 
          @click="mackNew"
          title="创建新模型"
          type="plus" class="anticon anticon-ok" />
        <div style="margin-left: auto;">
            <a-button style="margin: 10px;" icon="file-search" round @click="$refs.confinfo.init()" size="small">配置信息</a-button>
            <a-button style="margin: 10px;" icon="file-search" round @click="$refs.help.init()" size="small">帮助</a-button>
        </div>
      </template>
      <div
        v-removableCanvas
        id="efContainer"
        @dragover="(e)=>{e.preventDefault()}"
        @drop="onDrop"
        ref="efContainer"
        style="position: relative;flex:1;overflow: scroll;"
      >
        <div 
          v-for="(node,index) in nodeList" 
          class="node"
          :style="{top:node.top, left:node.left}" 
          :id="node.id"
        >
          <div class="node-box"
            :class="{open:true}"
          >
            <div class="node-box-show">
              <div class="node-box-left"></div>
              <div class="node-box-ico">
                <!-- <a-icon
                  class="anticon anticon-ok anticon-action"
                  :type="openindex===index?'fullscreen-exit':'fullscreen'"
                  :title="openindex===index?'关闭更多':'展开更多'"
                  @click.stop="openindex = (openindex === index? false:index)"
                /> -->
                <!-- 
                0:未执行
                1:执行中
                2:执行成功
                3:执行失败
                -1:编辑过
                receive: 受影响
                -->
                <a-icon
                  v-if="[0,3,-1].includes(node.status) ||
                    (node.receive && true)"
                  class="anticon anticon-ok anticon-action"
                  type="caret-right"
                  title="运行"
                  @click.stop="runthis(node)"
                />
                <a-icon
                  v-if="[2,-1].includes(node.status)"
                  class="anticon anticon-ok anticon-action"
                  type="eye"
                  title="查看报告"
                  @click.stop="getReport(node)"
                />
                <a-icon
                  v-if="node.module==8 && [2,-1].includes(node.status)"
                  class="anticon anticon-ok anticon-action"
                  type="eye"
                  title="查看结果"
                  @click.stop="()=>{
                    if(modeSample.id || node.module===0){
                      thisnode = node
                      visible = true
                    }
                  }"
                />
                <a-icon
                  class="anticon-ok anticon-action"
                  v-if="node.status!=1 && (modeSample.id || node.module==0) && ![8,9].includes(node.module)"
                  type="setting" 
                  title="配置参数"
                  @click.stop="()=>{
                    if(modeSample.id || node.module===0){
                      thisnode = node
                      visible = true
                    }
                  }"
                />
              </div>
              <!-- <div class="node-box-text" :show-overflow-tooltip="true">{{node.id}} </div> -->
              <a-tooltip placement="left">
                <template slot="title">
                  <span>{{statusToText(node)}}</span>
                </template>
                <div class="node-box-status" v-if="node.status">
                  <a-icon v-if="node.status === -1" 
                    type="edit"
                    :theme="node.receive?'outlined':'twoTone'" two-tone-color="#1cc4c4"/>
                  <a-icon v-else-if="node.status === 1"
                    type="loading"/>
                  <a-icon v-else-if="node.status === 2"
                    type="check-circle"
                    :theme="node.receive?'outlined':'twoTone'" two-tone-color="#52c41a"
                    />
                  <a-icon v-else-if="node.status === 3"
                    type="close-circle"
                    :theme="node.receive?'outlined':'twoTone'" two-tone-color="#eb2f96"
                    />
                </div>
              </a-tooltip>
            </div>
            <div class="node-box-confs">
              <div class="node-box-left"></div>
              <div class="node-box-more">
                <div class="node-box-text" :show-overflow-tooltip="true">{{node.id}} </div>
                <!-- <a-button @click.stop="runthis(node)" v-if="node.status === 0">运行</a-button>
                <a-button @click.stop="getReport(node)" v-else-if="node.status === 2">查看结果</a-button>
                <a-button @click.stop="runthis(node)" v-else-if="node.status === 3">重新执行</a-button> -->
              </div>
            </div>
          </div>
        </div>
        <div style="position:absolute;top: 2000px;left: 2000px;">&nbsp;</div>
      </div>
    </a-card>
    
    
    
    <conf-modal
      :thisnode="thisnode"
      :visible="visible"
      @cancel="visible=false;"
      @chooseModeSample="chooseModeSample"
      :sampleList="sampleList"
      :modeSample.sync="modeSample"
      :columnsData.sync="columnsData"
      :searchLoading="searchLoading"
      @search="searchLoading=true"
      @searchChange="searchChange"
      @delAll="changeDel(1)"
      @useAll="changeDel(0)"
      :modename="modename"
      
      :verifMode.sync="verifMode"
      :missing.sync="missing"
      :homogeny.sync="homogeny"
      :importanceAssessment.sync="importanceAssessment"
      :corr.sync="corr"
      :binn.sync="binn"
      :regre.sync="regre"
      :exchangeParam.sync="exchangeParam"
      :evaluate.sync="evaluate"
    />
    <help-modal ref="help"/>
    <confinfo-modal ref="confinfo" :confdata="allconf" />
    <save-modal ref="save" :modelInfo.sync="modelInfo" @MODEL_SAVE='MODEL_SAVE' />
    <transition name="modelMode">
      <div class="ant-modal-mask" v-show="reportVisible" @click="reportVisible = false;" ></div>
    </transition>
    <transition name="modelMode">
      <div class="modelMode" v-show="reportVisible">
        <div class="title">
          <div style="display: flex;">
            <span class="mastTitle">{{resultInfo.title}}</span>
          </div>
          <div style="display: flex;">
            <a-icon title="下载报告" type="download" @click="resultDownload"/>
            <!-- <a-icon title="保存报告" type="save" @click="resultSave"/> -->
            <a-icon title="关闭" @click="reportVisible = false;heightList=[]" type="close" />
          </div>
        </div>
        <div class="content">
          <div class="pdfPage" v-for="i in resultInfo.pageNum">
            <a-divider orientation="left" :id="`page:${i}`">
              <span>{{`page：${i}`}}</span>
            </a-divider>
            <v-pdf 
              ref="pdf" 
              :src="resultInfo.url" 
              :key="`${resultInfo.url}-page:${i}`" 
              :page="i"
            >
            </v-pdf>
          </div>
        </div>
      </div>
    </transition>
  </div>
</template>

<script>
import helpModal from './modules/modal_help'
import confinfoModal from './modules/modal_confinfo'
import confModal  from './modules/modal_conf'
import saveModal from './modules/modal_save'

import vPdf from 'vue-pdf'
import { jsPlumb } from 'jsplumb'
import { setting,connectOptions,sourceOptions,targetOptions } from './conf/options'
import { merge } from 'lodash'
import { getResults,getModelResult } from '@/axios/api'
import { exportFile,RANDOM } from '@/utils/functions.js'
import { mapGetters,mapActions } from 'vuex'

import confmixin from './mixins/use_conf'
import modelmixin from './mixins/use_model'
import { getStore,setStore,removeStore } from '@/utils/StoreOpers'
import { decode } from 'js-base64'
export default {
  components:{ confModal,vPdf,helpModal,confinfoModal,saveModal },
  data() {
    return {
      nodeList: [],
      lineList: [],
      jsPlumb:null,
      thisnode:{},
      visible:false,
      sampleList:[],
      openindex:false,
      reportVisible:false,
      // model:"AutoSC",
      resultInfo:{},
      STOP_TAG:false,
    }
  },
  mixins:[confmixin,modelmixin],
  created(){
    /**
     * Do important created in the mixins(use_model); Don't ask why;
     * And the props(by $route) also in mixins(use_model);
     * And this created will not wait for the "await";
     */
  },
  methods: {
    setStore,removeStore,
    statusToText({status,receive}){
      if(receive) return '受影响'
      switch(status){
        case 0: return '未执行'
        case 1: return '执行中'
        case 2: return '执行成功'
        case 3: return '执行失败'
        case -1: return '编辑过'
      }
    },
    changeStatus({index,status,node,module,id}){
      if(!node){
        if(index==undefined){
          if(module!=undefined) index = this.nodeList.findIndex(e=>e.module == module)
          else if(id!=undefined) index = this.nodeList.findIndex(e=>e.id == id)
        }
        node = this.nodeList[index]
        // console.log({nodeList:this.nodeList,node,module})
      }else if(index===undefined){
        index = this.nodeList.findIndex(e=>e.module == node.module)
      }
      if(!node) return
      node.status = status
      if(status == -1){
        // for(let i = index+1;i<this.nodeList.length;i++)
        for(const i in this.nodeList){
          if(i<=index) continue
          const afternode = this.nodeList[i]
          // if(afternode.status === 0) continue
          if(afternode.module === 9) continue
          afternode.receive = true
          console.log({afternode})
        }
      }else{
        node.receive = false
      }
      this.$set(this.nodeList,index,node)
    },
    async runCode(start,end){
      const NUMBER_OF_STEPS = this.nodeList.length - 1
      start = Number(start) | 1
      end = Number(end) | NUMBER_OF_STEPS
      if(start > end) return
      if(!this.modeSample.id){
        this.$message.error({content:`没有源数据`,key:`没有源数据`})
        return
      }
      if(this.$store.getters.IS_RUN){
        this.$message.error({content:`程序运行中,请稍后`,key:`程序运行中,请稍后`})
        return
      }
      if (isNaN(end)) end = NUMBER_OF_STEPS
      const runingmsg = this.$message.loading({
        content:'运行中...',
        duration:0,
        key:'LOADING_KEY'
      })
      this.cycleOperation(start,end)
        .then(e=>"执行成功")
        .catch(e=>String(e))
        .then(e=>{
          this.$message.info(e)
          runingmsg()
          this.runing = false
        })
    },
    async runthis(node){
      if(!this.modeSample.id){
        this.$message.error("请选择样本集")
        return
      }
      const { module } = node
      if(module === 0)
        this.chooseModeSample(this.modeSample,true)
      else if(module === 9)
        this.getResultUrl({node})
      else{
        const startindex = this.nodeList.findIndex(e=>e.module === module)
        for(let i=1;i<startindex;i++)
          if(this.nodeList[i].status != 2 || this.nodeList[i].receive){
            this.$message.error("请检查之前步骤是否完整")
            return
          }
        this.cycleOperation(startindex,startindex)
        .then(e=>"执行成功")
        .catch(e=>String(e))
        .then(e=>{
          this.$message.info(e)
          this.runing = false
        })
      }
    },
    async cycleOperation(start,end){
      for(let index=start;index<=end;index++){
        if(this.STOP_TAG){
          this.STOP_TAG = false
          return Promise.reject('主动停止运行')
        }
        const node = this.nodeList[index]
        const { module,status,receive } = node
        if((!this.runing &&(status===2 && !receive)) || module<1 || module>8)
          continue
        this.runing = true
        this.changeStatus({index,status:1,node})
        //*此处使用module
        await this.getResultAsync(module).then(async ({data})=>{
          this.changeStatus({index,status:2,node})
          if(module === 8){
            const { evaluate } = data
            console.log({data})
            this.evaluate = evaluate
            await this.addResultNode()
          }
        }).catch(e=>{
          console.log(e)
          this.changeStatus({index,status:3,node})
          throw new Error(`${index}发生异常:${e}`)
        })
      }
      return end
    },
    async stopCode(){
      this.$confirm({
        title: '停止程序的运行?',
        content: h => <div style='color:red;'>将于当前步骤运行完成之后停止</div>,
        onOk:()=>{ this.STOP_TAG = true },
        okText:'确认',
        cancelText:'取消'
      })
    },
    async addResultNode(url){
      const id = `结果-${(new Date()-1)+String(RANDOM(4))}`
      const {left,top,id:source} = this.nodeList.find(e=>e.module == 8)
      const node = {
        id,
        module:9,
        left:`calc(${left} + 400px)`,
        top,
      }
      this.nodeList.push(node)
      await this.$nextTick()
      
      if(!this.jsPlumb){
        this.jsPlumb = jsPlumb.getInstance()
        // this.jsPlumb.importDefaults(setting)
      }
      this.jsPlumb.ready(() => {
        this.jsPlumb.draggable(id, {
          containment: 'parent',
          stop: function (el) {
            console.log('拖拽结束: ', el)
          }
        })
        this.jsPlumb.connect({
          source,
          target: id,
        },connectOptions)
        console.log({connectOptions})
      })
      const index = this.nodeList.length-1
      this.getResultUrl({node,index,url})
    },
    async getResultUrl({node,index,url}){
      if(!index) index = this.nodeList.findIndex(e=>e.id === node.id)
      this.changeStatus({status:1,node,index})
      if(!url)
        url = await getModelResult({id:this.modelInfo.id}).then(e=>{
          if(Object.prototype.toString.call(e)=='[object Blob]'){
            const blob = new Blob([e],{type: 'application/pdf'})
            const url = window.URL.createObjectURL(blob)
            return url
          }
        })
      if(url){
        this.resultnode[node.id] = url
        this.changeStatus({status:2,node,index})
      }else{
        this.$message.error("获取结果结点失败")
        this.changeStatus({status:3,node,index})
      }
    },
    async getReport(node){
      const { id:nodeid,module } = node
      const { id,name:nodename } = this.modelInfo
      const msg = this.$message.loading("loading...")
      let url
      if(module===9){
        url = this.resultnode[nodeid]
        if(!url) url = await getModelResult({id:this.modelInfo.id}).then(e=>{
          if(Object.prototype.toString.call(e)=='[object Blob]'){
            const blob = new Blob([e],{type: 'application/pdf'})
            return window.URL.createObjectURL(blob)
          }
        }).catch(()=>{})
        this.resultnode[nodeid] = url
      }else{
        url = this.resultListSave[nodeid]
        url = await getResults({module,id}).then(e=>{
          if(Object.prototype.toString.call(e) == '[object Blob]'){
            const blob = new Blob([e],{type: 'application/pdf'})
            return window.URL.createObjectURL(blob)
          }
        }).catch(()=>{})
        this.resultListSave[nodeid] = url
      }
      if(url)
        await this.openPdf(nodeid,url)
      else
        this.$message.error("请求报告异常")
      msg()
    },
    resultDownload(){
      // console.log({resultInfo:this.resultInfo})
      const { resultInfo:{url,title:nodename},modelInfo:{name:modelname} } = this
      this.$message.info("下载模型结果")
      const fileName = `${modelname}-${nodename}-报告.pdf`
      exportFile(url,fileName)
    },
    openPdf(nodename,url){
      return new Promise((success)=>{
        vPdf.createLoadingTask(url).promise.then(pdf => {
          this.resultInfo = {
            title:nodename,
            url,
            pageNum: pdf.numPages
          }
          this.$nextTick(() => {//*翻到第一页
            this.reportVisible = true
            this.$nextTick(() => {
              document.getElementsByClassName('content')[0].scrollTop = 0
              success()
            })
          })
        }).catch((err) => {
          this.$message.error('pdf加载失败')
          success()
        })
      })
    },
    onDrop(e){
      e.preventDefault()
      e.returnValue = false
      const oFile=e.dataTransfer.files[0]
      if(oFile === undefined) return
      const { type,name } = oFile
      if(type !== "text/plain"){
        this.$message.error("不支持此类型配置文件")
        return
      }
      this.$confirm({
        title: '加载配置文件',
        content:(
          <p>
            是否加载参数配置文件:<strong>{name}</strong><br />
            这个操作会<span style='color:red'>覆盖</span>当前模型的参数,请谨慎操作
          </p>
        ),
        cancelText:'取消',
        okText:'确认',
        onOk:async () => {
          const reader = new FileReader()
          reader.onload = async e => {
            const loadingmsg = this.$message.loading("加载中",0)
            try{
              const { result } = e.srcElement || e.target
              const decode_result = JSON.parse(decode(result))
              console.log({decode_result})
              const { modeSample } = decode_result
              if(modeSample.id)
                await this.chooseModeSample(modeSample)
              this.allconf = decode_result//computed:set
              this.$message.success('加载成功')
              await this.$nextTick()
              loadingmsg()
            }catch(e){
              console.log(e)
              this.$message.error(`加载失败\n请检查配置文件`)
              await this.$nextTick()
              loadingmsg()
            }
          }
          reader.readAsText(oFile)
        },
        onCancel:async () => {
          
        }
      })
    },
    init(){
      if(!this.jsPlumb){
        this.jsPlumb = jsPlumb.getInstance()
        this.jsPlumb.importDefaults(setting)
      }
      this.jsPlumb.ready(() => {
        for (let item of this.nodeList) {
          // this.jsPlumb.makeSource(item.id, merge(sourceOptions, {}))
          // this.jsPlumb.makeTarget(item.id, targetOptions)
          if (!item.viewOnly) {
            this.jsPlumb.draggable(item.id, {
              containment: 'parent',
              // grid: [10, 10]//网格位置限制
            })
          }
        }
        for (let item of this.lineList) {
          this.jsPlumb.connect({
            source: item.from,
            target: item.to,
            label: item.label ? item.label : '',
          },connectOptions)
        }
        this.jsPlumb.setContainer(this.$refs.efContainer)
      })
    },
  },
  directives: {
    'removableCanvas': {
      bind(el, binding, vnode, oldNode) {
        console.log({el, binding, vnode, oldNode})
        if (!binding) {
          return
        }
        el.click = (e) => {
          console.log({e})
        }
        el.onmousedown = (e) => {
          if (e.button == 2) {
            // 右键不管
            return
          }
          let disX = e.clientX
          let disY = e.clientY
          el.style.cursor = 'move'
          document.onmousemove = function(e) {
            // console.log({el,e})
            e.preventDefault()
            const left = e.clientX - disX
            disX = e.clientX
            el.scrollLeft += -left
            const top = e.clientY - disY
            disY = e.clientY
            el.scrollTop += -top
          }
          document.onmouseup = function(e) {
            el.style.cursor = 'auto'
            document.onmousemove = null
            document.onmouseup = null
          }
        }
      }
    }
  },
}
</script>

<style lang="less" scoped>
@titleHeight: 40px;
@nodeHeight:32px;
@confsMaxHeight:300px;
/deep/.ant-card-head {
  height: @titleHeight;
  min-height: @titleHeight;
  font-weight: 700;
  display: flex;
  .ant-card-head-wrapper{
    width: 100%;
  }
  .ant-card-head-title{
    display: flex;
    align-items: center;
  }
  // .title {
  //   display: flex;
    
  // }
}
/deep/.ant-card-body {
  height: calc(100vh - @titleHeight - 56px);
  overflow-y: scroll;
}

.node{
  position: absolute;
  overflow: hidden;
  width: 170px;
  // 
  border-radius: 5px;
  background-color: #fff;
  box-sizing: border-box;
  &-error{
    background:#ff000029;
  }
  .open{
    max-height: calc(@confsMaxHeight + @nodeHeight);
  }
  &-box{
    transition: all .5s;
    max-height: @nodeHeight;
    padding-left: 8px;
    border: 1px solid #E0E3E7;
    &-show{
      display: flex;
      align-items:center;
      justify-content: space-between;
    }
    &-more{
      padding: 5px;
    }
    &:hover{
      cursor: move;
      background-color: #F0F7FF;
      // background-color: #F0F7FF;
      // border: 1px dashed #1879FF;
    }
    &-confs{
      // background-color: #ffaaff33;
      // padding: 5px;
      display: flex;
      // overflow-y: scroll;
      max-height: @confsMaxHeight;
    }
    
    &-left{
      position: absolute;
      top: 0;
      bottom: 0;
      left:0;
      width: 4px;
      background-color: #1879FF;
      margin-right: 8px;
    }
    &-ico{
      height:@nodeHeight;
      align-items:center;
      display: flex;
      &:hover{
        cursor: crosshair;
      }
    }
    &-text{
      line-height: @nodeHeight;
      color: #565758;
      font-size: 12px;
      // margin-left: 8px;
      // width: 100px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      text-align: center;
    }
    &-status{
      line-height: @nodeHeight;
      // position: absolute;
      // right: 5px;
      cursor: default;
    }
    
  }
}
</style>



<!-- 模型结果style -->
<style lang="less" scoped>
  @titleHeight: 40px;
  @contentPadding: 5px;
  @contentHeight: 10%; //contentHeight = (100% - 2*@contentHeight)
  @contentWidth: 10%; //contentWidth = (100% - 2*@contentWidth)
  @pdfPageDogEar: 80px; //pdf右上角折角

  .ant-modal-mask {
    //阴影
    z-index: 998 !important;
    // background-color: #000;
  }

  .modelMode {
    //模型展示
    position: absolute;
    top: @contentHeight;
    bottom: @contentHeight;
    left: @contentWidth;
    right: @contentWidth;
    // left: 50%;
    // transform: translate(-50%,0);
    z-index: 999;
    background: #fff;
    border-radius: 5px;
    display: flex;
    flex-flow: column nowrap;

    &-code {
      //展示代码
      bottom: 10%;
    }

    .title {
      height: @titleHeight;
      display: flex;
      width: calc(100% - @contentPadding - @contentPadding);
      justify-content: space-between;
      align-items: center;
      font-size: 24px;
      margin: 0 @contentPadding;
      user-select: none;
      -moz-user-select: none;
      -webkit-user-select: none;
      -ms-user-select: none;
      border-bottom: 1px solid rgba(0, 0, 0, 0.1);

      .mastTitle {
        //主标题
        margin-right: 10px;
      }

      .modeltitle {
        //小菜单
        transition: all .3;
        margin: 0 5px;

        &-select {}

        &-noselect {
          zoom: 0.7;
          -moz-transform: scale(0.5);
          opacity: 0.5;
          // align-self: flex-end;
          cursor: pointer;

          &:hover {
            opacity: 1;
          }
        }
      }
      .anticon {
        //关闭按钮
        padding: 3px;
        cursor: pointer;
        border-radius: 3px;
        align-self: center;
        margin-left: 20px;
        &:hover {
          background: rgba(0, 0, 0, 0.03);
          color: #000;
        }

        align-self: center;
      }
    }

    .content {
      /*    display: flex;
    justify-content: space-between; */
      height: calc(100% - @titleHeight);
      transition: all .3s ease;
      margin: 0 @contentPadding @contentPadding @contentPadding;
      overflow: scroll;

      .pdfPage {
        //pfd展示
        // >span {
        //   -webkit-clip-path: polygon(calc(100% - @pdfPageDogEar) 0px, 100% @pdfPageDogEar, 100% 100%, 0 100%, 0 0);
        //   clip-path: polygon(calc(100% - @pdfPageDogEar) 0px, 100% @pdfPageDogEar, 100% 100%, 0 100%, 0 0);
        // }
        .ant-divider {

          //分割线 - 不要剪切掉
          // &::before,
          &::after {
            border: none !important;
          }
        }
      }

      &::-webkit-scrollbar {
        height: 0px !important;
        width: 9px !important;
      }

      &::-webkit-scrollbar-thumb {
        border-radius: 0;
        border-style: dashed;
        background-color: rgba(157, 165, 183, 0.4);
        border-color: transparent;
        border-width: 2px;
        background-clip: padding-box;
      }

      &::-webkit-scrollbar-thumb:hover {
        background: rgba(157, 165, 183, 0.7)
      }
    }

    &-enter-active,
    &-leave-active {
      position: absolute;
      transition: all .5s ease;
    }

    &-enter,
    &-leave-to {
      position: absolute;
      transform: scale(0.5);
      transform: translate(-150%, -150%);
      opacity: 1;
    }
  }
</style>